#ifndef HPHGA_H
#define HPHGA_H

#include"stdlib.h"
#include"stdio.h"
#include"time.h"
#include"math.h"
#include"string.h"
#include<list>
#include"../neighbor_joining/neighbor_joining.h"
#include"useful_utils.h"
#include"useful_math.h"
#include"random.hpp"
#include<string.h>
#include"fitness.h"
//#include<jni.h>



using namespace std;

//build metrics
enum{AVERAGE_VALUE, MUTUAL_INFORMATION, MUTUAL_INFORMATION_ONLY_WORST, MUTUAL_INFORMATION_ONLY_BEST, MUTUAL_INFORMATION_WORST_PLUS_BEST, MIX_METRIC, PEARSON_COEFFICIENT, PEARSON_COEFFICIENT_V2, COVARIANCE, COVARIANCE_V2, DIFFERENCE};

//cut metrics
enum{AVERAGE_THRESHOLD, AVERAGE_THRESHOLD_NOT_CUT_LEAF_NODES, BB_CUT_FUNCTION, LAYER_CUT_FUNCTION, DISCONTINUITY_FUNCTION};

//tournaments
enum{SIMPLE_TOURNAMENT, EXCLUSIVE_TOURNAMENT};

//hillclimbing 
enum{DO_NOT_APPLY, NEW_POPULATION, SELECTED_POPULATION};

//ending type
enum{NO_ENDING, FUNCTION, MINOR_CCC, MINOR_1_MORE_CCC, MINOR_1_LESS_CCC};

#ifdef NOPRINTF

#define printf(x...)

#endif

#ifdef WEKA
//java
extern JNIEnv *javaenv;
extern jobject javaobj;

#endif


class phga
{
	public:
		phga(long array_size, int max_population_size, int tournament_type, float max_fraction_of_population_selected, int max_participants);
		phga(const char* filename, int argc, char** argv);
		phga();
		~phga();

		int generation;
		bool terminal_input;

		//auxiliary functions for setting and getting
		void setFitnessFunction(float (*fitness_pointer)(int* chromossome, int generation, long array_size));
		bool evolve(int population_size, float* fitness_result);
		bool checkStopCondition();
		float* result();
		void printResult();
		void printSimpleResult();
		void enableHillClimbing(float chance_of_hill_climbing, int apply_hill_climbing);
		void enableElitism();
		void enablePrintTreeEachGeneration();
		void setMetric(int build_metric, int cut_metric);
		void setK(float k);
		void enableFastLog();
		void setMutation(int percent);
		void setFatherMother();
		void setLayersProbability(float* layers_probability, int number_of_layers);
		void computeCCCs();
		void computeAndPrintCCCs();
		void setEnding(int ending_type, int (*ending_function)(int** possible_building_blocks, int* blocks, int array_size, float* cccs, int number_of_bbs));
		void setNeighborJoiningMetric(int metric);
		void onlyPrintNeighborJoiningData();
		void setNeighborJoiningRepetitions(int repetitions);
		void setNoise(float noise);
		void setModelBuildOption(int option);
		void setWekaFitness();//JNIEnv *env, jobject obj);
		float testFitness(int *chromossome, int generation, long array_size);
		void setMaxPopulation(int pop);
		void setFactor(float factor);
		void setProblemSize(int size);
		void setSeed(int seed);
		void setTournamentSize(int size);
		void allocate();
		void getParametersFromFile(const char* filename);

		//deprecated
		void oldVariation(int population_size);


		//structural intern functions
		void createRandomPopulation(int population_size);
		void createNextPopulation(int population_size);
		void populationSelection(int population_size);


	private:
		
		int max_participants; //in the tournament
		long array_size;
		int **population;
		int **best_population;
		int max_population_size;
		int selected_population_size;
		float max_fraction_of_population_selected;
		int* best_individual;
		int hill_climbing;
		bool print_tree;
		int tournament_type;
		bool use_fast_log;
		float **distance_matrix;
		float **tmp_distance_matrix;
		float chance_of_hill_climbing;
		bool elitism;
		float function_k;
		randomG random;
		int mutation_chance;
		int father_mother;
		float best_fit;
		bool print_BB;
		bool print_cccs;
		bool compute_cccs;
		int node_to_cut; //used for the DISCONTINUITY_FUNCTION cut metric
		int neighbor_joining_metric;
		bool only_print_neighbor_joining_data;
		int neighbor_joining_repetitions;
		float noise_value;
		int model_build_option;

		//float WekaFitness(int* chromossome, int generation, long array_size);
		float (*fitness_pointer)(int *chromossome, int generation, long array_size);

		void initialize();
		bool already_selected(int index, int selected_number, int* selected_index_array);
		long simple_tournament(int max_participants, int population_size);
		long exclusive_tournament(int max_participants, int population_size, int selected_number, int* selected_index_array);
		long loser_of_the_simple_tournament(int max_participants, int population_size);
		long loser_of_the_exclusive_tournament(int max_participants, int population_size, int selected_number, int* selected_index_array);
		void deleteSubsetList();
		bool checkStopCondition(int population_size);
		void normalizeDistanceMatrix();
		void hillClimbing(int* chromossome);
	
		//info
		void printBBs();


		//phylogenetic variables
		int* building_blocks;
		int** possible_building_blocks;
		int* possible_blocks;
		int number_of_bbs;
		int statistical_bbs;
		int blocks;
		int build_metric;
		int cut_metric;

		//phylogenetic extensions
		int number_of_layers_with_probability;
		float* layers_probability;
		float* cccs;
		int* generateAllPossibilities(int* building_block, int bbs);
		int (*ending_function)(int** possible_building_blocks, int* blocks, int array_size, float* cccs, int number_of_bbs);	
		int ending_type;

		//phylogenetic functions related
		float CCC(list<Graph_Node*>* tree_list, int selected_population_size);
		int cutTreeOnAverageThreshold(float maximum, float minimum, Graph_Node* root, int* building_blocks_chromossome);
		void recursiveCutTreeOnAverageThreshold(float threshold, list<Graph_Node*>* tree_list, Graph_Node* node);
		int cutTreeAsideFromLeafNodes(float maximum, float minimum, Graph_Node* root, int* building_blocks_chromossome);
		int applyBBFunction(Graph_Node* root, int* building_blocks_chromossome);
		int applyLayerFunction(Graph_Node* root, int* building_block);
		int applyLayerFunctionWithCCC(Graph_Node* root, int* building_block);
		int applyDiscontinuityFunction(Graph_Node* root, int* building_block, int node_to_cut);
		void recursiveCutTreeAsideFromLeafNodes(float threshold, list<Graph_Node*>* tree_list, Graph_Node* node);
		void recursiveSetBuildingBlock(int building_block_id, int* building_block, Graph_Node* node);
		float recursiveApplyBBFunction(list<Graph_Node*>* tree_list, Graph_Node* node, int* counter);
		void recursiveApplyLayerFunction(list<Graph_Node*>* tree_list, Graph_Node* node, int counter);
		void recursiveApplyDiscontinuityFunction(list<Graph_Node*>* tree_list, Graph_Node* node, int node_to_cut);

		//Core functions
		void buildDistanceMatrix(int population_size);
		float cutTree( Graph_Node* tree, bool* exit);

		//Metrics - for computing the distance between variables
		void valueMetric(int** population, int population_size, int array_size);
		void binaryMutualInformationMetric(int** population, int population_size, int array_size);
		void sumBinaryMutualInformationMetric(int** sample, int sample_size, int array_size);
		void mixMetric(int** population, int population_size, int array_size);
		void pearsonCoefficientMetric(int** sample, int sample_size, int array_size);
		void pearsonCoefficientV2Metric(int** sample, int sample_size, int array_size);
		void covarianceMetric(int** sample, int sample_size, int array_size);
		void covarianceV2Metric(int** sample, int sample_size, int array_size);
		void differenceMetric(int** sample, int sample_size, int array_size);
		
		//Metrics - for cutting the tree in building blocks
		int cutTreeSimple(int focus, Graph_Node* root, int* building_block);
		void recursiveCutTreeSimple(int focus, list<Graph_Node*>* tree_list, Graph_Node* node);

		//fast functions
		float fast_log2(float value);
		void prepare_log(float table_size);
		float* log_hash_table;
		float table_size;

		//debug	
		void printBestFitness(int population_size, float* best_fitness, float* avg_fitness);
		void calcBestFitness(int population_size, float* best_fitness, float* avg_fitness);
		void printSubsetList();
		void printSubsetListCC(int population_size);

};




#endif
